package hasthi.metamodel.resource;

import static hasthi.common.constants.WSDMConstants.EXTREME_MANAGEMENT_NS;
import static hasthi.common.constants.WSDMConstants.HOSTNAME_PARAM;
import static hasthi.common.constants.WSDMConstants.RESOURCE_CATEGORY_PARAM;
import static hasthi.common.constants.WSDMConstants.RESOURCE_NAME_PARAM;
import static hasthi.common.constants.WSDMConstants.RESOURCE_STATUS_PARAM;
import static hasthi.common.constants.WSDMConstants.*;
import static hasthi.metamodel.resource.ManagedResource.ResourceType.Service;
import static hasthi.metamodel.resource.ManagedResource.ResourceType.TransientService;
import hasthi.common.HasthiException;
import hasthi.common.InvaliedResourceDataException;
import hasthi.common.Utils;
import hasthi.common.XmlBeanUtils;
import hasthi.common.constants.WSDMConstants;
import hasthi.common.constants.WSDMConstants.SystemState;
import hasthi.manager.ManagedResourceType;

import java.util.Collection;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.xml.namespace.QName;

public abstract class AbstractManagedResource implements EditableManagedResource {

    protected  QName name;

    /**
     * Web Service URL via this service can be Managed
     */
    protected final String managerAgentURL;

    /**
     * Resource Type, as defined by managed System
     */
    protected String resourceType;

    /**
     * Resource type as defined by user
     */
    protected String userDefinedType;

    /**
     * Last time this resource was updated
     */
    protected long lastUpdated = System.currentTimeMillis();

    /**
     * Say is this master copy or a summary maintained
     */
    protected boolean fullcopy = true;

    protected final Map<String, String> properties;

    protected final ResourceModelContext modelContext;
    
    protected boolean active = true;

    AbstractManagedResource(ResourceModelContext modelContext, Map<QName, Object> extractedProperties,
            String managerAgentURL) throws InvaliedResourceDataException {
        try {
            this.modelContext = modelContext;
            properties = new ConcurrentHashMap<String, String>();
            this.managerAgentURL = managerAgentURL;
            for (QName propertyName : extractedProperties.keySet()) {
                initProperty(propertyName, extractedProperties.get(propertyName));
            }
            validate();
        } catch (HasthiException e) {
            throw new InvaliedResourceDataException("Error initializing resource ", e);
        }

    }

    AbstractManagedResource(ResourceModelContext modelContext,
            ManagedResourceType resourceDocument) throws InvaliedResourceDataException {
        try {
            properties = new ConcurrentHashMap<String, String>();
            this.managerAgentURL = resourceDocument.getManagementEndpoint();
            this.name = resourceDocument.getName();
            this.modelContext = modelContext;
//        Map<QName, Object> extractedProperties = XmlBeanUtils.extractProperties(resourceDocument);
//        extractProperties(extractedProperties);

            XmlBeanUtils.processProperties(resourceDocument, new XmlBeanUtils.ParameterProcessor() {
                public void process(QName name, Object value) throws HasthiException {
                    initProperty(name,value);
                }
            });
            validate();
        } catch (HasthiException e) {
            throw new InvaliedResourceDataException("Error initializing resource ", e);
        }

    }
/*
    private void extractProperties(Map<QName, Object> extractedProperties) throws InvaliedResourceDataException {
        try {
            for (QName propertyName : extractedProperties.keySet()) {
                // System.out.println(propertyName);
                if (RESOURCE_TYPE_PARAM.equals(propertyName)) {
                    this.userDefinedType = (String) extractedProperties.get(RESOURCE_TYPE_PARAM);
                } else if (RESOURCE_CATEGORY_PARAM.equals(propertyName)) {
                    this.resourceType = (String) extractedProperties.get(RESOURCE_CATEGORY_PARAM);
                } else if (RESOURCE_NAME_PARAM.equals(propertyName)) {
                    String resourceIDAsURI = (String) extractedProperties.get(propertyName);
                    name = Utils.uri2QName(resourceIDAsURI);
                }
                Object value = extractedProperties.get(propertyName);
                //We format the object value as a String
                String strValue = modelContext.getConverter().convert(value);
                this.properties.put(propertyName.getLocalPart(),strValue);
            }
            validate();
        } catch (HasthiException e) {
            throw new InvaliedResourceDataException(e.getMessage(),e);
        }
    }
*/
    private void initProperty(QName propertyName, Object value) throws HasthiException {
        if (RESOURCE_TYPE_PARAM.equals(propertyName)) {
            this.userDefinedType = (String) value;
        } else if (RESOURCE_CATEGORY_PARAM.equals(propertyName)) {
            this.resourceType = (String) value;
        } else if (RESOURCE_NAME_PARAM.equals(propertyName)) {
            String resourceIDAsURI = (String) value;
            name = Utils.uri2QName(resourceIDAsURI);
        }
        //We format the object value as a String
        String strValue = modelContext.getConverter().convert(value);
        this.properties.put(propertyName.getLocalPart(),strValue);
    }
   
    protected void validate() throws InvaliedResourceDataException{
        if (name == null) {
            throw new InvaliedResourceDataException("Attribute name must not be Null");
        }
        if(managerAgentURL == null){
            throw new InvaliedResourceDataException("Managment agent URL must not be Null");
        }
        
        if(resourceType == null){
            throw new InvaliedResourceDataException("Resource type must not be Null");
        }
        if((resourceType.equals(ResourceType.TransientService.toString()) 
                || resourceType.equals(ResourceType.Service.toString())) && userDefinedType == null){
            throw new InvaliedResourceDataException("User defined type must not be Null");
        }
        lastUpdated =  System.currentTimeMillis();
    }
    
    public AbstractManagedResource(QName name, ResourceModelContext modelContext,
            String managerAgentURL, String resourceType, String userDefinedType)
            throws InvaliedResourceDataException {
        this.name = name;
        this.modelContext = modelContext;
        this.managerAgentURL = managerAgentURL;
        this.resourceType = resourceType;
        this.userDefinedType = userDefinedType;
        properties = new Hashtable<String, String>();
        properties.put(RESOURCE_CATEGORY_PARAM.getLocalPart(), getCategory());
        properties.put(RESOURCE_TYPE_PARAM.getLocalPart(), getType());
        validate();
    }

    public abstract String getProperty(String name) throws HasthiException;

    public Collection<String> getProperties() {
        return properties.keySet();
    }

    public String getState() throws HasthiException {
        return (String) getProperty(RESOURCE_STATUS_PARAM.getLocalPart());
    }

    public QName getQName() {
        return name;
    }

    public String getManagerAgentURL() {
        return managerAgentURL;
    }

    public String getCategory() {
        return resourceType;
    }

    public String getType() {
        return userDefinedType;
    }

    @Override
    public boolean equals(Object obj) {
        //return super.equals(obj);
       
        try {
            if (obj instanceof ManagedResource) {
                ManagedResource other = ((ManagedResource) obj);
                Collection<String> otherGuysPropertyNames = other.getProperties();
                if (otherGuysPropertyNames.size() != properties.size()) {
                    return false;
                }
                for (String otherGuysProperty : otherGuysPropertyNames) {
                    if(!other.getProperty(otherGuysProperty).equals(getProperty(otherGuysProperty))){
                       return false;
                    }
                }
                return true;
            } else {
                return false;
            }
        } catch (HasthiException e) {
            e.printStackTrace();
            return false;
        }
    }
    
    @Override
    public int hashCode() {
        return super.hashCode();
    }
    

    public ManagedResourceType toXml(boolean summery) throws HasthiException {
        ManagedResourceType xmlResource = ManagedResourceType.Factory.newInstance();
        if(summery){
            String propertyName = RESOURCE_STATUS_PARAM.getLocalPart(); 
            XmlBeanUtils.addNameValuePair(xmlResource, new QName(
                    EXTREME_MANAGEMENT_NS, propertyName), properties.get(propertyName));
            propertyName = RESOURCE_CATEGORY_PARAM.getLocalPart(); 
            XmlBeanUtils.addNameValuePair(xmlResource, new QName(
                    EXTREME_MANAGEMENT_NS, propertyName), properties.get(propertyName));
            if(TransientService.toString().equals(getCategory()) 
                    || Service.toString().equals(getCategory())){
                propertyName = RESOURCE_TYPE_PARAM.getLocalPart(); 
                XmlBeanUtils.addNameValuePair(xmlResource, new QName(
                        EXTREME_MANAGEMENT_NS, propertyName), properties.get(propertyName));
                propertyName = HOSTNAME_PARAM.getLocalPart(); 
                XmlBeanUtils.addNameValuePair(xmlResource, new QName(
                        EXTREME_MANAGEMENT_NS, propertyName), properties.get(propertyName));
            }
            String group = getGroup();
            if(group != null){
                String paramName = GROUP_PARAM.getLocalPart();
                XmlBeanUtils.addNameValuePair(xmlResource, new QName(
                        EXTREME_MANAGEMENT_NS, paramName), properties.get(paramName));
            }
        }else{
            for (String propertyName : properties.keySet()) {
                Object value = getProperty(propertyName);
                XmlBeanUtils.addNameValuePair(xmlResource, new QName(
                        EXTREME_MANAGEMENT_NS, propertyName), value);
            }
        }
        xmlResource.setName(getQName());
        xmlResource.setManagementEndpoint(getManagerAgentURL());
        return xmlResource;
    }

    public long getLastUpdated() {
        return lastUpdated;
    }

    public String getName() {
        return getQName().toString();
    }

    @Override
    public String toString() {
        StringBuffer buffer = new StringBuffer();
        buffer.append(getName());
        buffer.append(properties.toString());
        return buffer.toString();
    }

    public void setLastUpdated(long lastUpdated) {
        this.lastUpdated = lastUpdated;
    }

    public void setProperty(String name, Object value) throws HasthiException {
        throw new UnsupportedOperationException();
        
    }

    public void setState(SystemState state) throws HasthiException {
        throw new UnsupportedOperationException();
    }
    
    public String getGroup() throws HasthiException {
        return getProperty(GROUP_PARAM.getLocalPart());
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }
}